﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Framework.Core.Collections;
using Framework.Core.Dependency.Adapter;
using Framework.Core.Dependency.Domain;
using StreamReader = Framework.Core.Dependency.Adapter.StreamReader;

namespace Framework.Core.Dependency.Parser
{
    internal class CampainParser : IDirectoryParser<Solution>
    {
        private static readonly Regex RgxOutputFolder = new Regex(@"\$\{campain\.outputfolder\}\\(?<Part>[^\\]*)",
                                                                  RegexOptions.Compiled | RegexOptions.CultureInvariant |
                                                                  RegexOptions.IgnoreCase);

        private readonly IDirectoryReader directoryReader;
        private readonly IStreamReader streamReader;

        private CampainParser(IDirectoryReader directoryReader, IStreamReader streamReader)
        {
            this.directoryReader = directoryReader;
            this.streamReader = streamReader;
        }

        public CampainParser()
            : this(new DirectoryReader(), new StreamReader())
        {
        }

        public IEnumerable<Solution> Parse(string root, string branch)
        {
            var excluded = new List<string> { "TI_RAD", "SONAR", "DependencyAnalysis", "CTY_SQL", "CTY_SDK" };

            var campainXmls = directoryReader.GetFiles(root, "*Campain.xml")
                .Where(c => !excluded.Any(c.Contains))
                .ToList();

            var solutions = new ConcurrentList<Solution>();

            Parallel.ForEach(
                campainXmls,
                campainXml =>
                {
                    using (var stream = streamReader.GetStream(campainXml))
                    {
                        using (var reader = XmlReader.Create(stream))
                        {
                            var campainDocument = XDocument.Load(reader);

                            // ReSharper disable PossibleNullReferenceException
                            var nsAttribute = campainDocument.Root.Attribute("xmlns");
                            // ReSharper restore PossibleNullReferenceException
                            var ns = XNamespace.Get(nsAttribute == null ? string.Empty : nsAttribute.Value);

                            var parsedSolutions =
                                campainDocument
                                    .Descendants(ns + "builditem")
                                    .Where(d => d.Attribute("name") != null)
                                    .Select(d => GetSolution(root, branch, d, ns));

                            solutions.AddRange(parsedSolutions);
                        }
                    }
                });

            return solutions;
        }

        private static Solution GetSolution(string root, string branch, XElement d, XNamespace ns)
        {
            // ReSharper disable PossibleNullReferenceException
            var solutionName = GetName(d);
            var solutionPath = GetPath(root, branch, d, solutionName, "path");
            var solution =
                new Solution(
                    Path.GetFileNameWithoutExtension(solutionName),
                    Path.GetExtension(solutionName),
                    solutionPath,
                    Language.Unknown,
                    root);

            d.Parent.Descendants(ns + "test").ToList()
             .ForEach(
                 t =>
                 {
                     if (t.Attribute("name") == null)
                     {
                         return;
                     }
                     var projectName =
                         Path.GetFileName(t.Attribute("name").Value);
                     var projectNameWithoutExtension =
                         Path.GetFileNameWithoutExtension(projectName);
                     var unitTestProject =
                         new Project(projectNameWithoutExtension,
                                     Path.GetExtension(projectName),
                                     "",
                                     projectNameWithoutExtension,
                                     Language.Unknown)
                             {
                                 IsUnitTest =
                                     true
                             };
                     solution.AddProject(unitTestProject, root);
                 });

            return solution;
        }

        private static string GetName(XElement d)
        {
            var projectName = d.Attribute("name").Value;
            return projectName;
        }

        private static string GetPath(string root, string branch, XElement d, string projectName, string attribute)
        {
            var projectPath = Path.Combine(ReplaceVariableWithLocalPath(d.Attribute(attribute).Value, root, branch),
                                           Path.GetFileName(projectName));
            return projectPath.Replace("/", @"\");
        }

        private static string ReplaceVariableWithLocalPath(string value, string root, string branch)
        {
            var result = value
                .Replace("${mapping.framework.local}", GetPath(root, @"CTY.Framework", branch))
                .Replace("${mapping.referential.local}", GetPath(root, @"CTY.Referential", branch))
                .Replace("${mapping.meteor.local}", GetPath(root, @"CTY.Meteor", branch))
                .Replace("${mapping.feeder.local}", GetPath(root, @"CTY.Feeder", branch))
                .Replace("${mapping.index.local}", GetPath(root, @"CTY.Index", branch))
                .Replace("${mapping.natgas.local}", GetPath(root, @"CTY.NatGas", branch))
                .Replace("${mapping.meteorbo.local}", GetPath(root, @"CTY.MeteorBO", branch))
                .Replace("${mapping.meteorsales.local}", GetPath(root, @"CTY.MeteorSales", branch))
                .Replace("${mapping.rad.local}", GetPath(root, @"CTY.RAD", branch))
                .Replace("${mapping.mirador.local}", GetPath(root, @"CTY.Mirador", branch));

            return result;

            //return RgxOutputFolder.Replace(result, match => match.Groups["Part"].Value + @"\Main\Source");
        }

        private static string GetPath(string root, string teamProject, string branch)
        {
            return Path.Combine(Path.Combine(root, teamProject), branch);
        }
    }
}